<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of BApp
 *
 * @author ID59C
 */
class BApp extends BNode {

    const App_SpaceEngine = 'SpaceEngine';
    const Not_Load_By_ClassName = 'not_load_by_class_name';
    const All_App = 'all_app';
    const Not_Exist_App = "not_exist_app";
    const Not_Reload = 'not_reload';

    protected $uiManager = null;
    protected $dbAdapter = null;
    protected $config = null;
    public $name = 'not specify';
    public $description = null;
    public $spaceId = null;
    public $className = 'not specify';
    public $isImported = false;
    protected $currentOperation = null;
    protected $currentOperationFlow = null;
    protected $currentSpace = null;
    protected $isPreLoadFlag = false;
    protected $hostApp;
    public $preLoadAppList = array();
    protected $isPreLoaded = false;

    public function __construct($data = null, $context = NULL) {
        $this->className = get_class($this);
        //if (get_class($this) != 'BApp')
        //by default, not load app
        //if ($data != NULL)
        $this->load($data, $context);
    }

    public function initImport() {

        parent::initImport();

        $cm = $this->getConfigManager();

        $basePath = $cm->getConfigValue('basePath');

        //default import items
        $imports = array(
            'classes' => 'classes',
            'cooker' => 'cooker',
            'config' => 'config',
            'models' => 'models',
            'control' => 'ui.controls'
        );

        foreach ($imports as $importItem) {
            if (is_string($importItem)) {
                $importPath = $basePath . '.' . $importItem . '.*';
                yii::import($importPath);
            }
        }
        $this->isImported = TRUE;

        if (!$this->isPreLoaded) {
            $preLoadConfig = $cm->getConfig('preLoad');

            if ($preLoadConfig != NULL) {
                if (is_array($preLoadConfig)) {
                    if (isset($preLoadConfig['app'])) {
                        $preLoadList = $preLoadConfig['app'];
                        foreach ($preLoadList as $appClass => $config) {
                            //here will import preload app
                            $app = new $appClass();
                            $this->preLoadAppList[$app->getId()] = $app;
                            $app->setAsPreLoad($this);
                        }
                    }
                }
                $this->isPreLoaded = TRUE;
            }
        }
    }

    public function getConfigManager($data = NULL) {
        if ($this->configManager == NULL) {
            $this->configManager = new BConfigManager($this);

            $className = get_class($this);
            $basePath = 'application.application.app.' . $className;
            $this->configManager->addConfig('basePath', $basePath);
            $this->configManager->initBasePath();
            $config = $this->configManager;
            //if config changed, reload it
            if ($config->isConfigFileChanged(array('scope' => BContextObj::Scope_App)))
                $this->onRegister();
            //$config->importConfigFromConfigFile();
        }

        return $this->configManager;
    }

    public function setAsPreLoad($hostApp) {
        if ($hostApp instanceof BApp) {
            $this->hostApp = $hostApp;
            $this->isPreLoadFlag = TRUE;
        }
    }

    public function getHostApp($data = NULL) {
        return $this->hostApp;
    }

    public function isPreLoad($data = NULL) {
        return $this->isPreLoadFlag;
    }

    public function load($data = NULL, $context = NULL) {
        if (get_class($this) === 'BApp') {
            return; //do nothing            
        } else if ($data != NULL && $data === self::Not_Load_By_ClassName) {
            return;
        } else {
            if (!$this->isImported) {
                $this->initImport();
                $this->isImported = TRUE;
            }
        }

        $class = get_class($this);
        //$apps = Blyn::app()->apps;
        $isLoaded = FALSE;
        //when execute new class()
        if ($data == NULL || !is_numeric($data)) {
            if (isset(Blyn::app()->apps[$class])) {
                $app = Blyn::app()->apps[$class];
            } else {
                if ($this->id == NULL) {
                    //$this->className = get_class($this);
                    $app = BApp::find(array('className' => $this->className));
                }
            }
            if (!$app instanceof BApp || $app->getId() == NULL) {
                //$this->load($app->getId());
                //means not find app in database, need to create it in database
                $config = $this->getConfigManager();
                $name = $config->getConfigValue('appMeta.appName');
                $description = $config->getConfigValue('appMeta.description');
                $className = get_class($this);
                $this->name = $name == NULL ? $className : $name;
                $this->description = $description == NULL ? $className : $description;
                $this->save();
            } else {
                if ($app != NULL && $app->getId() != NULL) {
                    $properties = get_object_vars($this);
                    foreach ($properties as $property => $value) {
                        $this->$property = $app->$property;
                    }
                }
            }
            $isLoaded = TRUE;
        }

        if (is_numeric($data) && $data > 0)
            $this->id = $data;

        if ($this->id != NULL && $this->id > 0 && !$isLoaded) {
            parent::load($this->id);
        }

        if ($this->getId() != NULL)
            Blyn::app()->apps[$class] = $this;
    }

    public function getUIManager($data = NULL) {
        if ($this->uiManager == NULL) {
            $this->uiManager = new BUIApp($this);
        }
        return $this->uiManager;
    }

    public function getDbAdapter($data = NULL) {
        if ($this->dbAdapter == NULL)
            $this->dbAdapter = new BDbAdapter($this);

        return $this->dbAdapter;
    }

    //use this function to find public messenger during app
    public static function getMessenger($data = NULL) {

        $appClass = get_called_class();

        $app = new $appClass();

        if ($app instanceof BApp) {
            $operation = $app->getOperation($data);
            if ($operation instanceof BOperation) {
                return $operation->getMessenger();
            }
        }
    }

    /**
     * @return type array
     * @param type $data
     */
    public function getOperationFlows($data = NULL) {

        $spaceId = isset($data['spaceId']) ? $data['spaceId'] : Blyn::app()->getActiveSpace()->getId();
        $appId = isset($data['appId']) ? $data['appId'] : $this->getId();

        $flows = BOperationFlow::findAll(array('spaceId' => $spaceId, 'appId' => $appId));

        if (is_array($flows)) {
            foreach ($flows as $flow) {
                $flow->setCurrentApp($this);
            }
        }

        return $flows;
    }

    public function getOperationFlow($data = NULL) {

        $name = NULL;
        $flowId = null;

        if (is_string($data)) {
            $name = $data;
        }

        if (is_numeric($data) && $data != 0)
            $flowId = $data;

        $flows = $this->getOperationFlows();

        foreach ($flows as $item) {
            if ($item->getName() == $name)
                return $item;
            if ($item->getId() == $flowId)
                return $item;
        }

        return FALSE;
    }

    public function addOperation($data = NULL) {

        $spaceId = $this->spaceId == 0 || $this->spaceId == NULL ? Blyn::app()->getActiveSpace()->getId() : $this->spaceId;

        if ($data instanceof BOperation) {
            $op = $data;
            if ($op->appId == $this->getId() && ($op->spaceId == 0 || $op->spaceId == $spaceId ))
                return $op;
            else {
                //clone operation and assign to current app
                $op = new BOperation();
                $op->name = $data->name;
                $op->description = $data->description;
                $op->data = $data->data;
                $op->spaceId = $data->spaceId;
                $op->appId = $this->getId();
                $op->save();
                return $op;
            }
        }

        if (is_array($data)) {
            $attributes = $data;
            $attributes['appId'] = $this->getId();
            if (!isset($attributes['spaceId']))
                $attributes['spaceId'] = $spaceId;

            if (isset($data['class']) && $data['class'] != NULL)
                $class = $data['class'];
            $op = new $class();
            $op->save($attributes);

            return $op;
        }
    }

    public function addOperationFlow($data = NULL) {

        $flow = NULL;
        $flow2 = null;
        $attributes = array();

        $spaceId = isset($data['spaceId']) ? $data['spaceId'] : Blyn::app()->getActiveSpace()->getId();
        $appId = isset($data['appId']) ? $data['appId'] : $this->getId();

        //save exist operationflow into
        if ($flow instanceof BOperationFlow) {
            if (!(($flow->spaceId == $spaceId || $flow->spaceId == 0) && $flow->appId == $appId)) {
                $flow2 = new BOperationFlow();
                //$count = count($this->getOperationFlows());
                $attributes['name'] = $flow->getName();
                $attributes['description'] = $flow->description;
                $attributes['relationship'] = $flow->relationship;
                $attributes['spaceId'] = $spaceId;
                $attributes['appId'] = $appId;
                return $flow2->save($attributes);
            }
        }

        if (is_array($data)) {//create a new operationflow and save it
            $flow2 = new BOperationFlow();
            $count = count($this->getOperationFlows());

            $attributes['name'] = isset($data['name']) ? $data['name'] : "opflow_" . $spaceId . "_" . $appId . "_" . $count;
            $attributes['description'] = isset($data['description']) ? $data['description'] : "no description";
            if (isset($data['relationship']))
                $attributes['relationship'] = $data['relationship'];
            $attributes['spaceId'] = isset($data['spaceId']) ? $data['spaceId'] : $spaceId;
            $attributes['appId'] = $appId;
        }
        if ($attributes != NULL) {
            $result = $flow2->save($attributes);
            if ($result != NULL && $flow2 != NULL)
                return $flow2;
        }
        return FALSE;
    }

    public function getOperations($data = NULL) {
        $spaceId = isset($data['spaceId']) ? $data['spaceId'] : $this->getCurrentSpace()->getId();
        $appId = isset($data['appId']) ? $data['appId'] : $this->getId();

        $ops = BOperation::findAll(array('spaceId' => $spaceId, 'appId' => $appId));

        if (is_array($ops)) {
            foreach ($ops as $op) {
                $op->setCurrentApp($this);
            }
        }

        if (isset($data['user'])) {
            $userOperations = array();
            if (is_numeric($data['user']))
                $user = new BUser($data['user']);
            $user = $data['user'];

            if ($user instanceof BUser) {
                foreach ($ops as $op) {
                    //here role is app (for ex BWeSport) role , actually we need check BSpaceEngine role becuase 
                    // there are only BSpaceEngine role in resouce_permit_role table
                    if (BPermit::checkUserPermit($op, BPermit::AllowAccess, $user))
                        $userOperations[$op->getId()] = $op;
                    else if (BPermit::checkUserRoleParentPermit($op, BPermit::AllowAccess, $user))
                        $userOperations[$op->getId()] = $op;
                }
            }

            //if ($userOperations != NULL)
            return $userOperations;
        }

        if ($ops == NULL)
            $ops = array();

        return $ops;
    }

    /**
     * use this function to get operation for app, if set $new, can get a new operation
     * for app
     * @param type $data
     */
    public function getOperation($data = NULL, $addNew = FALSE) {
        $name = NULL;
        $opId = null;

        if (is_string($data)) {
            $name = $data;
        }

        if (is_numeric($data) && $data != 0)
            $opId = $data;

        $ops = $this->getOperations();

        foreach ($ops as $item) {
            if ($item->getName() == $name || get_class($item) == $name)
                return $item;
            if ($item->getId() == $opId)
                return $item;
        }

        return FALSE;
    }

    /**
     * 
     * @param type $data
     * @return BSpace
     */
    public function getCurrentSpace($data = NULL) {
        if (!$this->currentSpace instanceof BSpace)
            $this->currentSpace = Blyn::app()->getActiveSpace();
        return $this->currentSpace;
    }

    public function setCurrentSpace($space) {
        $this->currentSpace = $space;
    }

    public function getCurrentOperation($data = NULL) {
        if ($this->currentOperation instanceof BOperation)
            $this->currentOperation->setCurrentApp($this);
        return $this->currentOperation;
    }

    public function setCurrentOperation($operation) {
        $this->currentOperation = $operation;
    }

    public function getCurrentOperationFlow($data = NULL) {
        if ($this->currentOperationFlow instanceof BOperationFlow)
            $this->currentOperationFlow->setCurrentApp($this);
        return $this->currentOperationFlow;
    }

    public function setCurrentOperationFlow($operationFlow) {
        $this->currentOperationFlow = $operationFlow;
    }

    private function initRoles($data = NULL) {
        $spaceId = isset($data['spaceId']) ? $data['spaceId'] : Blyn::app()->getActiveSpace()->getId();
        $appId = isset($data['appId']) ? $data['appId'] : $this->getId();

        //add app roles into space
        if (!$this instanceof BSpaceEngine) {
            $spaceEngine = Blyn::app()->getActiveSpace()->getSpaceEngine();
            $spaceEngineAppAdmin = $spaceEngine->getRole(BRole::Role_App_Admin);
            //$appAdmin = $this->getRole(BRole::Role_App_Admin);
            $appAdmin = $spaceEngineAppAdmin->addChildItem(array('name' => BRole::Role_App_Admin, 'spaceId' => $spaceId, 'appId' => $appId));

            $spaceEngineAppMember = $spaceEngine->getRole(BRole::Role_App_Member);
            //$appMember = $this->getRole(BRole::Role_App_Member);
            $appMember = $spaceEngineAppMember->addChildItem(array('name' => BRole::Role_App_Member, 'spaceId' => $spaceId, 'appId' => $appId));

            $spaceEngineAppCustomer = $spaceEngine->getRole(BRole::Role_App_customer);
            //$appCustomer = $this->getRole(BRole::Role_App_customer);
            $appCustomer = $spaceEngineAppCustomer->addChildItem(array('name' => BRole::Role_App_customer, 'spaceId' => $spaceId, 'appId' => $appId));
        } else {
            $appRootContext = new BRole();
            $appRootContext->name = BRole::Role_App_Root;
            $appRootContext->spaceId = $spaceId;
            $appRootContext->appId = $appId;
            //$appRoot->save();

            $root = BRole::getRoot();
            $appRoot = $root->getChild($appRootContext);
            if ($appRoot == NULL)
                $appRoot = $root->addChildItem($appRootContext);

            $appAdminContext = new BRole();
            $appAdminContext->name = BRole::Role_App_Admin;
            $appAdminContext->spaceId = $spaceId;
            $appAdminContext->appId = $appId;


            $appAdmin = $appRoot->addChildItem($appAdminContext);

            $appMemberContext = new BRole();
            $appMemberContext->name = BRole::Role_App_Member;
            $appMemberContext->spaceId = $spaceId;
            $appMemberContext->appId = $appId;

            $appMember = $appRoot->addChildItem($appMemberContext);

            $appCustomerContext = new BRole();
            $appCustomerContext->name = BRole::Role_App_customer;
            $appCustomerContext->spaceId = $spaceId;
            $appCustomerContext->appId = $appId;

            $appCustomer = $appRoot->addChildItem($appCustomerContext);

            $roleGroup = BRole::find(BRole::Role_Group);

            if (!$roleGroup instanceof BRole) {
                $roleGroup = new BRole();
                $roleGroup->name = BRole::Role_Group;
                BRole::getRoot()->addChild($roleGroup);
            }


            $allowJoinRole = $roleGroup->getChild(BRole::Role_Group_AllowJoinRole);
            if (!$allowJoinRole instanceof BRole) {
                $allowJoinRole = new BRole();
                $allowJoinRole->name = BRole::Role_Group_AllowJoinRole;
                //$allowJoinRoleGroup->save();
                $roleGroup->addChild($allowJoinRole);
            }

            //by default, add member and customer role as allowJoinRole
            //$allowJoinRole->addChild($appCustomerContext);
            //$allowJoinRole->addChild($appMemberContext);

            $autoJoinRole = $roleGroup->getChild(BRole::Role_Group_AutoJoinRole);
            if (!$autoJoinRole instanceof BRole) {
                $autoJoinRole = new BRole();
                $autoJoinRole->name = BRole::Role_Group_AutoJoinRole;
                //$allowJoinRoleGroup->save();
                $roleGroup->addChild($autoJoinRole);
            }

            //by default, add member and customer role as allowJoinRole
            //$autoJoinRole->addChild($appCustomerContext);
            //$allowJoinRole->addChild($appCustomerContext);
        }
    }

    public function getRoles($data = NULL, $recursive = FALSE) {
        $spaceId = isset($data['spaceId']) ? $data['spaceId'] : $this->getCurrentSpace()->getId();
        $appId = isset($data['appId']) ? $data['appId'] : $this->getId();

        $appRootRole = BRole::find(array('name' => BRole::Role_App_Root, 'spaceId' => $spaceId));

        if (isset($data['type'])) {
            if ($data['type'] == BRole::Role_App_customer) {
                $appRole = $appRootRole->getChild(BRole::Role_App_customer);
                $appRoles = $appRole->getChildren(NULL, $recursive);
                $appRoles[$appRole->getId()] = $appRole;
                return $appRoles;
            }
            if ($data['type'] == BRole::Role_App_Admin) {
                $appRole = $appRootRole->getChild(BRole::Role_App_Admin);
                $appRoles = $appRole->getChildren(NULL, $recursive);
                $appRoles[$appRole->getId()] = $appRole;
                return $appRoles;
            }
            if ($data['type'] == BRole::Role_App_Member) {
                $appRole = $appRootRole->getChild(BRole::Role_App_Member);
                $appRoles = $appRole->getChildren(NULL, $recursive);
                $appRoles[$appRole->getId()] = $appRole;
                return $appRoles;
            }
        }

        if ($appRootRole != NULL) {
            $roles = $appRootRole->getChildren(NULL, $recursive);

            if ($roles != NULL)
                return $roles;
        }

        return NULL;
    }

    //under app, there are three root role: app_admin, app_workteam, app_customer
    /**
     *
     * @param type $data
     * @return BRole
     */
    public function getRole($data = NULL) {

        $name = NULL;

        if (is_string($data))
            $name = $data;

        if (is_array($data))
            $name = isset($data['name']) ? $data['name'] : NULL;

        if ($data instanceof BRole)
            $name = $data->getName();

        $context = new BRole();

        if ($this->spaceId > 0)
            $spaceId = $this->spaceId;
        else if ($this->getCurrentSpace() instanceof BSpace && $this->getCurrentSpace()->getId() != NULL)
            $spaceId = $this->getCurrentSpace()->getId();
        else {
            $spaceId = Blyn::app()->getActiveSpace()->getId();
        }

        //$spaceId = $this->getCurrentSpace() == NULL || $this->spaceId == 0 ? Blyn::app()->getActiveSpace()->getId() : $this->getCurrentSpace()->getId();
        $appId = $this->getId();

        $context->spaceId = $spaceId;
        $context->appId = $appId;

        if ($name != NULL) {
            if ($name == BRole::Role_Space_Admin) {
                $name = BRole::Role_App_Admin;
                if (!$this instanceof BSpaceEngine) {
                    $spaceEngine = new BSpaceEngine();
                    $spaceEngine->spaceId = $this->spaceId;
                    return $spaceEngine->getRole($name);
                }
            }
            if ($name == BRole::Role_Space_customer) {
                $name = BRole::Role_App_customer;
                if (!$this instanceof BSpaceEngine) {
                    $spaceEngine = new BSpaceEngine();
                    $spaceEngine->spaceId = $this->spaceId;
                    return $spaceEngine->getRole($name);
                }
            }
            if ($name == BRole::Role_Space_Member) {
                $name = BRole::Role_App_Member;
                if (!$this instanceof BSpaceEngine) {
                    $spaceEngine = new BSpaceEngine();
                    $spaceEngine->spaceId = $this->spaceId;
                    return $spaceEngine->getRole($name);
                }
            }
            $context->name = $name;
        }

        //search app_root node
        //first find role in app scope
        //$appRoot = BRole::find(array('name' => BRole::Role_App_Root, 'appId' => $this->getId(), 'spaceId' => $spaceId));
        $allRoles = BRole::findAll(array('spaceId' => $spaceId), TRUE);

        foreach ($allRoles as $role) {
            if ($role->getName() == $name && $role->appId == $this->getId()) {
                return $role;
            }
        }

        //otherwise, search from global scope
        $role = BRole::find($data);

        if ($role instanceof BRole)
            return $role;

        //return BRole::find($context);

        return FALSE;
    }

    public function getCircles($data = NULL) {
        $spaceId = isset($data['spaceId']) ? $data['spaceId'] : Blyn::app()->getActiveSpace()->getId();
        $appId = isset($data['appId']) ? $data['appId'] : $this->getId();

        $circles = BCircle::findAll(array('spaceId' => $spaceId, 'appId' => $appId));

        return $circles;
    }

    //root circle for app: app_circle_root
    public function getCircle($data) {
        $name = NULL;
        $id = null;

        if (is_string($data)) {
            $name = $data;
        }

        if (is_numeric($data) && $data != 0)
            $id = $data;

        $items = $this->getCircles();

        foreach ($items as $item) {
            if ($item->getName() == $name)
                return $item;
            if ($item->getId() == $id)
                return $item;
        }

        return FALSE;
    }

    //file root for app: app_file_root
    public function getFileResources($data = NULL) {
        $spaceId = isset($data['spaceId']) ? $data['spaceId'] : Blyn::app()->getActiveSpace()->getId();
        $appId = isset($data['appId']) ? $data['appId'] : $this->getId();

        $files = BFileResource::findAll(array('spaceId' => $spaceId, 'appId' => $appId));

        return $files;
    }

    //root circle for app: app_circle_root
    public function getFile($data) {
        $name = NULL;
        $id = null;

        if (is_string($data)) {
            $name = $data;
        }

        if (is_numeric($data) && $data != 0)
            $id = $data;

        $items = $this->getFileResources();

        foreach ($items as $item) {
            if ($item->getName() == $name)
                return $item;
            if ($item->getId() == $id)
                return $item;
        }

        return FALSE;
    }

    /**
     * this function is called when add App to appStore
     * and update App meta data files
     * @param type $data
     */
    public function onRegisterEvent($event) {
        $this->raiseEvent("onRegisterEvent", $event);
    }

    //this is event handler for register event
    protected function onRegister($event = NULL) {

        //only register when app id >0
        if ($this->getId() == NULL || !$this->getId() > 0)
            return;

        //before register, we need set active app if not set
        //if (Blyn::app()->getActiveApp() == NULL)
        //Blyn::app()->setActiveApp($this);
        $config = $this->getConfigManager();
        //$configRoot = $config->getRootNode();
        //init roles
        $eventData = array();
        if ($event != NULL && $event->sender != NULL) {
            $space = $event->sender instanceof BSpace ? $event->sender : NULL;
            $eventData = array('spaceId' => $space->getId());
        }

        $this->initRoles($eventData);

        $appRoles = $config->getConfigList('appMeta.roles', TRUE);
        $adminRole = $this->getRole(BRole::Role_App_Admin);
        $customerRole = $this->getRole(BRole::Role_App_customer);
        $memberRole = $this->getRole(BRole::Role_App_Member);
        if (isset($appRoles['admin'])) {
            $roles = $appRoles['admin'];
            if (is_string($roles))
                $roles = explode(',', $roles);
            $adminRole->addRecursiveChildren($roles);
        }
        if (isset($appRoles['customer'])) {
            $roles = $appRoles['customer'];
            if (is_string($roles))
                $roles = explode(',', $roles);
            $customerRole->addRecursiveChildren($roles);
            //$customerRole->addRecursiveChildren($appRoles['customer']);
        }
        if (isset($appRoles['member'])) {
            $roles = $appRoles['member'];
            if (is_string($roles))
                $roles = explode(',', $roles);
            $memberRole->addRecursiveChildren($roles);
            //$memberRole->addRecursiveChildren($appRoles['member']);
        }

        $configOperations = $config->getConfigList('appMeta.operations');

        foreach ($configOperations as $key => $operation) {
            if ($operation instanceof BConfig)
                $name = $operation->getName();
            if (is_array($operation))
                $name = $key;
            $this->registerOperation($operation, $name);
        }
        //add operation permit

        $configOperationFlows = $config->getConfigList('appMeta.operationFlows');

        if (is_array($configOperationFlows)) {
            //if operationFlow exist, do nothing. otherwise, save new operation
            foreach ($configOperationFlows as $key => $operationFlow) {
                $this->registerOperationFlow($operationFlow, $key);
            }
        }
    }

    private function registerOperation($operation = NULL, $operationName = NULL) {

        if ($operation == NULL && $operationName == NULL)
            return FALSE;

        //set default value
        $operationDescription = NULL;
        $permits = array();

        if ($operation instanceof BConfig) {
            $operationName = $operation->getName();
            $operationClass = $operation->getConfig('class')->getValue();
            $displayName = $operation->getConfig('displayName')->getValue();
            //$operationConfigs = $operation->getConfigList(NULL, TRUE);            
            $operationDescription = $operation->getConfig('description') == NULL ? 'no description' : $operation->getConfig('description')->getValue();
            $permits = $operation->getConfigList('permit');
        }

        if (is_array($operation)) {
            $operationName = isset($operation['name']) ? $operation['name'] : $operationName;
            $operationClass = isset($operation['class']) ? $operation['class'] : 'BOperation';
            $operationDescription = isset($operation['description']) ? $operation['description'] : $operationDescription;
            $permits = isset($operation['permit']) ? $operation['permit'] : $permits;
        }

        //set default permits for operation
        if ($permits == NULL)
            $permits = array(BPermit::AllowAccess => BRole::All_Roles);
        if ($operationDescription == NULL)
            $operationDescription = $operationName;

        $spaceId = $this->spaceId == 0 || $this->spaceId == NULL ? Blyn::app()->getActiveSpace()->getId() : $this->spaceId;
        $appId = $this->getId();
        //$groupId = BRoleGroup::find(array('name'=>$groupName,'appId'=>  $this->getId()));
        $sOperations = BOperation::findAll(array('name' => $operationName, 'appId' => $appId, 'spaceId' => $spaceId));
        $isExitOperation = FALSE;
        if ($sOperations != NULL) {
            foreach ($sOperations as $operation) {
                if ($operation->spaceId === 0 || $operation->spaceId == $spaceId) {
                    $isExitOperation = TRUE;
                    break; //means role exist already
                }
            }
        }

        if (!$isExitOperation) {
            $operation = $this->addOperation(array('name' => $operationName, 'class' => $operationClass, 'description' => $operationDescription));
            $operationGroup = BOperationGroup::find(BOperationGroup::group_configurable_operation);
            $operationGroup->addMember($operation);

            //add operation permit
            if (is_array($permits)) {
                foreach ($permits as $key => $permitConfig) {
                    $isAddedPermit = FALSE;
                    if ($permitConfig instanceof BConfig) {
                        $permit = $permitConfig->getName();
                        $role = $permitConfig->getValue();
                    }
                    if (is_string($permitConfig)) {
                        $permit = $key;
                        $role = $permitConfig;
                    }
                    if (is_array($permitConfig)) {
                        $permit = $key;
                        foreach ($permitConfig as $role) {
                            BPermit::addPermit($operation, $permit, $role, $this);
                        }
                        $isAddedPermit = TRUE;
                    }

                    //$role = $this->getRole($roleData);
                    if ($isAddedPermit == FALSE)
                        BPermit::addPermit($operation, $permit, $role, $this);
                }
            }
            //register operation messages
        }
        if ($operation != NULL)
            return $operation;
        return FALSE;
    }

    private function registerOperationFlow($operationFlow, $name = NULL) {
        $flowName = isset($operationFlow['name']) ? $operationFlow['name'] : $name;
        $flowDescription = isset($operationFlow['description']) ? $operationFlow['description'] : $flowName;
        //$spaceId = $this->spaceId;
        $appId = $this->getId();
        //$groupId = BRoleGroup::find(array('name'=>$groupName,'appId'=>  $this->getId()));
        $sFlows = BOperationFlow::findAll(array('name' => $flowName, 'appId' => $appId));
        $isExitFlow = FALSE;
        if ($sFlows != NULL) {
            foreach ($sFlows as $flow) {
                if ($flow->spaceId === 0 || $flow->spaceId == Blyn::app()->getActiveSpace()->getId()) {
                    $isExitFlow = TRUE;
                    break; //means role exist already
                }
            }
        }

        if (!$isExitFlow) {
            $flow = $this->addOperationFlow(array('name' => $flowName, 'description' => $flowDescription));
            $flowGroup = BOperationFlowGroup::find(BOperationFlowGroup::group_configurable_operationFlow);
            $flowGroup->addMember($flow);
        }

        $operations = isset($operationFlow['operations']) ? $operationFlow['operations'] : array();

        foreach ($operations as $key => $operation) {
            $operation = $this->registerOperation($operation, $key);
            if ($operation != NULL && $flow != NULL)
                $flow->addOperation($operation);
        }
    }

    //this is event handler for addToSpace event
    public function onAddToSpace($event) {
        $app = $this;
        if ($event->sender != NULL)
            $space = $event->sender;
        $app->onRegister($event);
    }

    //this is event hander for removeFrom Space event
    protected function onRemoveFromSpace($data = NULL) {
        
    }

    public function save($context = null) {

        $context = BRepository::getContext($context);

        $context->modelClass = 'BMApp';
        $saveItems = array();
        $saveItems['classname'] = get_class($this);
        $context->saveItems = $saveItems;

        //$this->id = BRepositoryFactory::createRepository()->saveEntity($this, $context);

        if ($this->id == null && parent::save($context)) {
            //     if (Blyn::app()->getActiveApp() == NULL)
            //         Blyn::app()->setActiveApp($this);
            $event = new CEvent($this);
            $this->onRegisterEvent($event);
        }
    }

    public function run($data = NULL) {
        
    }

    /**
     * build message instance from application level
     * @param type $message, message data such as name, title
     * @param type $operationMessage, can be operation or one of message type under operation
     * @return \BMessage
     */
    public static function buildMessage($operation, $messageName = NULL, $message = NULL) {

        $appClass = get_called_class();
        $app = new $appClass();

        if (is_string($operation)) {
            $operation = $app->getOperation($operation);
        }

        if ($operation instanceof BOperation) {
            return $operation->getMessenger()->createMessage($message, $messageName = null);
        }

        return false;
    }

    /**
     * 
     * @param type $id
     * @return BApp
     */
    public static function getAppById($id) {
        //$apps = Blyn::app()->apps;
        //if (isset($apps[$id]))
        //return $apps[$id];

        if ($id > 0) {
            $repository = BRepositoryFactory::createRepository2();
            $appContext = new BApp();
            $appContext->id = $id;
            $repository->loadEntity($appContext);
            $app = $appContext;
            if ($app != NULL) {
                Blyn::app()->apps[$id] = $app;
                return $app;
            }
        }

        return FALSE;
    }
    
    public static function getAppClassById($id){
        $model = new BMApp();
        $row = $model->findByPk($id);
        $appClass = $row->classname;
        return $appClass;
    }

    public function getFormModel($data) {
        if (is_string($data)) {
            $formModelClass = $data;
            if (class_exists($formModelClass)) {
                return new $formModelClass;
            }
        }

        if (is_array($data)) {
            $formModelClass = NULL;
            $scenario = NULL;
            if (isset($data[0]) && is_string($data[0]))
                $formModelClass = $data[0];
            //$formModelClass = NULL;
            if (isset($data['name']))
                $formModelClass = $data['name'];
            if (isset($data['class']))
                $formModelClass = $data['class'];

            if (isset($data[1]) && is_string($data[1])) {
                $scenario = $data[1];
            }
            if (isset($data['scenario']))
                $scenario = $data['scenario'];

            if ($formModelClass != NULL && class_exists($formModelClass)) {
                if ($scenario != NULL)
                    return new $formModelClass($scenario);
                else
                    return new $formModelClass();
            }

            return FALSE;
        }
    }

    /*
      public function loadData($loadItems = array()) {
      parent::loadData($loadItems);
      parent::initImport();
      }
     * 
     */
}

?>
